package org.coldswap.tracker;
import net.contentobjects.jnotify.JNotifyListener;
import org.coldswap.instrumentation.ClassInstrumenter;
import org.coldswap.transformer.ClassRedefiner;
import org.coldswap.transformer.ReferenceReplacerManager;
import org.coldswap.util.ByteCodeClassLoader;
import org.coldswap.util.ClassUtil;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.ClassNode;
import java.io.File;
import java.io.FileFilter;
import java.lang.instrument.ClassDefinition;
import java.lang.instrument.Instrumentation;
import java.lang.instrument.UnmodifiableClassException;
import java.util.Map;
import java.util.logging.Logger;
/**
* (C) Copyright 2013 Faur Ioan-Aurel.
* <p/>
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Lesser General Public License
* (LGPL) version 2.1 which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/lgpl-2.1.html
* <p/>
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
* <p/>
* Contributors:
* faur
* <p/>
* Created at:
* 5:53 PM 3/17/13
*/
/**
* Listen for any class file modifications and instrument
* JVM class loader to reload any modification that has been
* made to the byte code.
*/
public class ClassListener implements JNotifyListener {
private final static String sep = System.getProperty("file.separator");
private final static Logger logger = Logger.getLogger(ClassListener.class.getName());
private final int maxNumberOfMethods;
private ReferenceReplacerManager refManager = ReferenceReplacerManager.getInstance();
static {
logger.setLevel(ClassUtil.logLevel);
}
public ClassListener(int maxNumberOfMethods) {
this.maxNumberOfMethods = maxNumberOfMethods;
}
private final FileFilter filter = new ClassFileFilter();
@Override
public void fileCreated(int i, String s, String s2) {
//pass
}
@Override
public void fileDeleted(int i, String s, String s2) {
//pass
}
@Override
public void fileModified(int i, String root, String className) {
if ((!"".equals(className)) && (null != className)) if (filter.accept(new File(className))) {
Map<String, Class<?>> loaded = ClassInstrumenter.getInstance().getLoadedClasses();
byte[] bytes = ByteCodeClassLoader.loadClassBytes(root + sep + className);
// first eight bytes for versioning
if (bytes.length < 8) {
return;
}
ClassNode classNode = new ClassNode(Opcodes.ASM5);
ClassReader classReader = new ClassReader(bytes);
classReader.accept(classNode, 0);
String clsName = classNode.name.replace('/', '.').replace(".class", "");
logger.info("Class " + clsName + " has been modified on the disk");
if ((!"".equals(clsName)) && (null != clsName)) {
Class<?> clazz = loaded.get(clsName);
if (clazz == null) {
logger.info(clsName + " is new class file!");
try {
Class<?> cla = Class.forName(clsName);
// run reference replacer
bytes = refManager.runReferenceReplacer(bytes);
ClassDefinition def = new ClassDefinition(cla, bytes);
Instrumentation inst = ClassInstrumenter.getInstance().getInstrumenter();
inst.redefineClasses(def);
} catch (ClassNotFoundException e) {
// it means that the class was not loaded yet into machine
// so no worries here
} catch (UnmodifiableClassException e) {
logger.warning(toString());
}
return;
}
ClassRedefiner redefiner = new ClassRedefiner(clsName, root + sep + className, maxNumberOfMethods);
try {
redefiner.redefineClass(clazz);
} catch (UnmodifiableClassException e) {
logger.severe(e.toString());
} catch (ClassNotFoundException e) {
logger.severe(e.toString());
}
}
}
}
@Override
public void fileRenamed(int i, String s, String s2, String s3) {
//pass
}
}